package com.java.Tomcat;

import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TomcatServer {
    public static void main(String[] args) {
        new TomcatServer().startServer();
    }

    public void startServer() {
        // 解析Xml配置文件内的配置
        Map<String, Map<String, String>> serverXmlMap = parseServerXml();

        // 获取Tomcat端口号
        int port = 8080;
        if (serverXmlMap.get("Connector").get("port") != null) {
            port = Integer.parseInt(serverXmlMap.get("Connector").get("port"));
        }

        // 获取线程池是否开启配置
        boolean openThreadPool = false;
        if (serverXmlMap.get("Connector").get("executor") != null && "true".equalsIgnoreCase(serverXmlMap.get("Connector").get("executor"))) {
            openThreadPool = true;
        }

        // 创建线程池
        ThreadPoolExecutor threadPoolExecutor = null;
        if (openThreadPool) {
            int corePoolSize = Runtime.getRuntime().availableProcessors();
            int maxPoolSize = corePoolSize * 2;
            long keepAliveTime = 10;
            TimeUnit unit = TimeUnit.SECONDS;
            BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(maxPoolSize * 4);
            ThreadFactory threadFactory = new ThreadFactory() {
                private final AtomicInteger id = new AtomicInteger(1);

                @Override
                public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r, "线程" + id.getAndIncrement());
                    System.out.println(thread.getName() + "已被创建");
                    return thread;
                }
            };
            RejectedExecutionHandler handler = new RejectedExecutionHandler() {
                @Override
                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                    System.out.println(r.toString() + "通过拒绝策略被拒绝");
                }
            };
            threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);

            threadPoolExecutor.prestartAllCoreThreads(); // 预启动所有核心线程
        }

        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("服务" + serverSocket.getLocalPort() + "成功启动");
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println("客户端" + socket.getPort() + "连接成功");

                if (openThreadPool) {
                    threadPoolExecutor.execute(new ServiceTask(socket));
                } else {
                    Thread t = new Thread(new ServiceTask(socket));
                    t.start();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (openThreadPool && threadPoolExecutor != null) {
                threadPoolExecutor.shutdown();
            }
        }
    }

    private Map<String, Map<String, String>> parseServerXml() {
        Map<String, Map<String, String>> result = new ConcurrentHashMap<>();
        try {
            // 创建文档工厂对象
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            // 构建器
            DocumentBuilder builder = factory.newDocumentBuilder();

            // 文件路径
            String serverXmlPath = System.getProperty("user.dir") + File.separator + "conf" + File.separator + "server.xml";

            // 解析
            NodeList nodeList = builder.parse(serverXmlPath).getElementsByTagName("Connector");
            for (int i = 0; i < nodeList.getLength(); i++) {
                Element element = (Element) nodeList.item(i);

                Map<String, String> map = new ConcurrentHashMap<>();
                map.put("port", element.getAttribute("port"));
                map.put("executor", element.getAttribute("executor"));

                result.put("Connector", map);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    static class ServiceTask implements Runnable {
        private Socket socket;
        private String connection = "close";

        public ServiceTask(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            boolean circulate = true;
            while (circulate) {
                InputStream inputStream = null;
                OutputStream outputStream = null;
                try {
                    inputStream = socket.getInputStream();
                    outputStream = socket.getOutputStream();

                    // 解析HTTP请求
                    MyHttpServletRequest request = new MyHttpServletRequest(socket, inputStream);
                    // 读取请求文件内容并拼接协议响应
                    MyHttpServletResponse response = new MyHttpServletResponse(request, outputStream);
                } catch (IOException e) {
                    e.printStackTrace();
                    circulate = false;
                } finally {
                    // 短链接则直接关闭，长连接则无需关闭
                    if ("close".equals(connection)) {
                        circulate = false;
                        if (inputStream != null) {
                            try {
                                inputStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        if (outputStream != null) {
                            try {
                                outputStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        if (socket != null) {
                            try {
                                socket.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }

        @Override
        public String toString() {
            return Integer.toString(socket.getPort());
        }
    }
}
